package org.windowkit.toolkit.icons;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;

import javax.swing.Icon;
import javax.swing.SwingConstants;

/**
 * This class allows rotatable icons to easily be created by automating the
 * rotation and trnaslation process. Extend this class and implement drawIcon.
 * Draw the icon in this method considering the default direction to be east.
 * When any other direction is chosen upon instantiation the icon will be
 * rotated so that the initially eastward pointing icon will now be drawn
 * pointed towards the chosen direction.
 *
 * @author Eric Lundin
 *
 */
public abstract class RotatableIcon implements Icon {
	/**
	 * The direction this icon will face.
	 */
	int direction;

	/**
	 * The height of the icon.
	 */
	private double height;

	/**
	 * The width of the icon.
	 */
	private double width;

	public RotatableIcon(final int direction) {
		switch (direction) {
		case SwingConstants.NORTH:
		case SwingConstants.SOUTH:
		case SwingConstants.EAST:
		case SwingConstants.WEST:
			this.direction = direction;
			break;
		default:
			throw new IllegalArgumentException(
					"Arrow Icon direction must be one of: SwingConstants.NORTH, "
							+ "SwingConstants.SOUTH, SwingConstants.EAST,"
							+ " or SwingConstants.WEST ");
		}
	}

	/**
	 * Tranlsates the graphics origin (0,0) in the center of the components
	 * space. Then translates the origin back to its original place after the
	 * abstract method drawIcon has completed.
	 */
	@SuppressWarnings("unused")
	public void paintIcon(final Component c, final Graphics g, final int x, final int y) {
		Dimension centerPoint = findCenter(c);
		Graphics2D g2 = (Graphics2D) g;
		RenderingHints renderHints = new RenderingHints(
				RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		renderHints.put(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_QUALITY);
		g2.setRenderingHints(renderHints);
		setIconHeight(height = c.getSize().getHeight());
		setIconWidth(c.getSize().getWidth());
		g.translate(centerPoint.width, centerPoint.height);
		rotateToDirection(g2, 0, 0);
		drawIcon(c, g2);
		g.translate(-centerPoint.width, -centerPoint.height);

	}

	/**
	 * Set the height of the icon. By default this gets set to height/2.
	 * override if the icon needs to fill more area.
	 *
	 * @param height
	 */
	public void setIconHeight(final double height) {
		this.height = height / 2;
	}

	/**
	 * By default the width parameter gets set to width/2. Override if the icon
	 * needs to fill more area.
	 *
	 * @param width
	 *            The width of the icon.
	 */
	public void setIconWidth(final double width) {
		this.width = width / 2;
	}

	/**
	 * Implement this method drawing the desired icon pointed eastwards. This
	 * super class has already performed the rotation and translations. Consider
	 * the center of the component (0,0) and draw the desired icon pointed
	 * eastward.
	 *
	 * @param c
	 *            The component to draw the icon on. Useful for getting sizes,
	 *            colors, etc.
	 * @param g2
	 *            The graphics object to use. Has already been rotated and
	 *            translated so that (0,0) is at the center of the component to
	 *            paint the icon on.
	 */
	public abstract void drawIcon(Component c, Graphics2D g2);

	/**
	 * Finds the center of a given component.
	 *
	 * @param c
	 *            The component to find the center of.
	 * @return Dimension representing the (x,y) coordinates of the center of
	 *         this component.
	 */
	private Dimension findCenter(final Component c) {
		Dimension d = new Dimension();
		d.setSize(c.getSize().getWidth() / 2, c.getSize().getHeight() / 2);
		return d;
	}

	/**
	 * Rotate the graphics object so our westward facing arrow points the
	 * correct way.
	 *
	 * @param g
	 *            The graphics object used to draw the icon.
	 */
	public void rotateToDirection(final Graphics2D g, final int x, final int y) {
		double rotateAmount = 0;
		switch (direction) {
		case SwingConstants.NORTH:
			rotateAmount = -90d;
			break;
		case SwingConstants.SOUTH:
			rotateAmount = 90d;
			break;
		case SwingConstants.WEST:
			rotateAmount = 180d;
			break;
		case SwingConstants.EAST:
			return;
		}
		g.rotate(Math.toRadians(rotateAmount), x, y);
	}

	public int getIconWidth() {
		return (int) width;
	}

	public int getIconHeight() {
		return (int) height;
	}

	/**
	 * Returns the maximum amount of space to either side of the origin in the Y
	 * direction when centered.
	 *
	 * @return The y limit.
	 */
	public int getYLimit() {
		return getIconHeight() / 2;
	}

	/**
	 * Returns the maximum amount of space to either side of the origin in the X
	 * direction when centered.
	 *
	 * @return The x limit.
	 */
	public int getXLimit() {
		return getIconWidth() / 2;
	}
}
